/** @file vbo.h
	@brief VertexBufferObjects */

#ifndef G3_VBO_H
#define G3_VBO_H

#include "math/m_vector.h"
#include "material.h"

namespace G3 {

/** @class VBO
	@brief VertexBufferObject class*/
class VBO: public LOGGABLE {
    public:
        /**
            @brief A constructor
        */
        VBO ();
        /**
            A destructor
        */
        ~VBO ();

        //! Pointer to the vertices
        G3::VEC3F *m_vertices;
        //! Pointer to the texture coordinates
        G3::VEC2F *m_texcoords;
        //! Pointer to the normals
        G3::VEC3F *m_normals;
        //! Pointer to the tangents
        G3::VEC3F *m_tangents;
        //! Number of vertices
        uint m_num_vertices;
        //! Number of indexes
        uint m_num_indexes;
        //! Pointer to the indexes
        uint *m_indexes;
        //! Number of materials
        uint m_num_materials;
        //! Pointer to materials
        G3::MATERIAL *m_materials;

        int m_reserved_memory;

        //! Buffer id lookup numbers
        enum BUF_ID {
            //! Vertex buffer
            VBO_VERTEX_BUF = 0,
            //! Index buffer
            VBO_INDEX_BUF = 1,
            //! Texture coordinate buffer
            VBO_TEXCOORD_BUF = 2,
            //! Normal buffer
            VBO_NORMAL_BUF = 3
        };

        /**
            @brief Binds the VBO to current rendering
            @return True on success, false on failure
            \todo Upload tangents ?
        */
        bool bind ();
        /**
            @brief Unbinds the VBO from current rendering
            @return True on success, false on failure
        */
        bool unbind ();

        /**
            @brief Binds a material
            @param[in] mat_id Material ID
            @return True on success, false on failure
        */
        bool bind_material (int i_mat_id);
        /**
            @brief Unbinds the last bound material
            @return True on success, false on failure
        */
        bool unbind_material ();

        /**
            @brief Initializes the VBO buffers
            @param[in] nv Number of vertices
            @param[in] v Pointer to the vertices
            @param[in] nid Number of indexes
            @param[in] id Pointer to the indexes
            @param[in] nm Number of materials
            @param[in] m Pointer to the materials
            @param[in] uv Pointer to the texture coordinates (defaults to NULL)
            @param[in] n Pointer to the normals  (defaults to NULL)
            @param[in] t Pointer to the tangents (defaults to NULL)
            \todo Tangent buffers ?
        */
        void init (uint nv, G3::VEC3F *v, uint nid, uint *id, uint nm, G3::MATERIAL *m = NULL, G3::VEC2F *uv = NULL, G3::VEC3F *n = NULL, G3::VEC3F *t = NULL);

        /**
            @brief Rebuilds the VBO
        */
        void build ();

        /**
            @brief Updates the uploaded data
            @param[in] id Which buffer is to be updated ?
            @param[in] offset Offset to the region to be updated
            @param[in] size The size of that region to be updated
            @param[in] data Pointer to the fresh data to be uploaded
        */
        void update (BUF_ID e_id, int i_offset, int i_size, void *p_data);

        /**
            @brief Dumps the VBO into the log
        */
        void dump();

    protected:
        //! Is the VBO initialized or not
        bool m_initialized;
        //! VBO buffer identificators
        int m_buffer_ids [5];
        //! VBO buffer id lookup table
        int m_buffer_lookup [5];
        //! Number of VBO buffer id-s
        int m_num_buffers;

        //! Is a material applied
        bool m_material_applied;
        //! Index of the last material
        uint m_current_material;

        /**
            @brief Allocates memory for the VBO on the graphics card
            @param[in] i_num_verts Number of vertices to allocate memory for
            @param[in] b_buf_static Is the buffer static or is it dynamic?
        */
        void alloc (int i_num_verts, bool b_buf_static = true);

        /**
            @brief Clears the buffers
        */
        void clear ();
};

}

#endif // G3_VBO_H
